package com.cqrcb.cloud.service.quota;

import com.cqrcb.cloud.config.redis.annotation.CustomCachePut;
import com.cqrcb.cloud.config.redis.annotation.CustomCacheable;
import com.cqrcb.cloud.config.redis.annotation.CustomLock;
import com.cqrcb.cloud.entity.api.req.Q;
import com.cqrcb.cloud.entity.api.res.R;
import com.cqrcb.cloud.entity.constant.MarketConstant;
import com.cqrcb.cloud.entity.constant.ThreadConstant;
import com.cqrcb.cloud.entity.exception.CustomException;
import com.cqrcb.cloud.entity.exception.ExceptionMember;
import com.cqrcb.cloud.entity.quota.MultiQuotaCtrlMarketMain;
import com.cqrcb.cloud.entity.quota.io.MultiQuotaCtrlMarketMainIO;
import com.cqrcb.cloud.entity.quota.io.base.QuotaBaseIO;
import com.cqrcb.cloud.mapper.quota.MultiQuotaCtrlMarketMainMapper;
import com.cqrcb.cloud.service.quota.base.QuotaBaseServiceImpl;
import com.cqrcb.cloud.service.quota.thread.DeductionQuotaThread;
import com.cqrcb.cloud.utils.BigDecimalUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.redisson.connection.ConnectionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import java.math.BigDecimal;


/**
 * MultiQuotaCtrlMarketMainImpl
 *
 * @author yangxueyong
 * @date 2021/06/08
 */
@Slf4j
@DubboService
public class MultiQuotaCtrlMarketMainImpl extends QuotaBaseServiceImpl implements MultiQuotaCtrlMarketMainService {
    @Autowired
    private MultiQuotaCtrlMarketMainMapper multiQuotaCtrlMarketMainMapper;

    @Autowired
    private MultiQuotaCtrlMarketMainService multiQuotaCtrlMarketMainService;

    @Autowired
    @Qualifier("multiQuotaCtrlMarketMainImpl")
    private QuotaBaseServiceImpl quotaBaseService;

    @Override
    public boolean saveFinancialProductQuota(MultiQuotaCtrlMarketMain record) {
        return false;
    }

    @Override
    public int updateByPrimaryKeySelective(MultiQuotaCtrlMarketMain record) {
        return multiQuotaCtrlMarketMainMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public R deductionFinancialProductQuota(Q<MultiQuotaCtrlMarketMainIO> recordq) {
        return deductionFinancialProductQuota(recordq.getBody().getType(), recordq, MultiQuotaCtrlMarketMainIO.class);
    }

    /**
     * 扣除金融产品配额的核心
     *
     * @param t 记录
     * @return {@link MultiQuotaCtrlMarketMain}
     */
    @Override
    public MultiQuotaCtrlMarketMain deductionFinancialProductQuotaCore(QuotaBaseIO t) {
//        ConnectionListener
        MultiQuotaCtrlMarketMainIO record = (MultiQuotaCtrlMarketMainIO)t;
        //查询目标对象的额度信息（从redis中查询）
        quotaBaseService.getMultiQuotaCtrlMarketMainForRedis(record);
        //扣减额度
        MultiQuotaCtrlMarketMain multiQuotaCtrlMarketMain = multiQuotaCtrlMarketMainService.deductionQuota(record);
        //扣减完成之后，将扣减记录异步到数据库，使用mq服务(目前使用线程去更新)
        ThreadConstant.addQueue(new DeductionQuotaThread(record, quotaBaseService, multiQuotaCtrlMarketMainService));
        return multiQuotaCtrlMarketMain;
    }


    /**
     * 扣除配额
     *
     * @param record 记录
     * @return {@link R}
     */
    @Override
    @CustomLock(key = "'quota:' + #record.type + ':' + #record.dataType + ':' + #record.dataId",waitTime = 5,leaseTime = 300)
    public MultiQuotaCtrlMarketMain deductionQuota(MultiQuotaCtrlMarketMainIO record){
        MultiQuotaCtrlMarketMain multiQuotaCtrlMarketMain = (MultiQuotaCtrlMarketMain) quotaBaseService.getMultiQuotaCtrlMarketMainForRedis(record);
        if(multiQuotaCtrlMarketMain == null){
            throw new CustomException(ExceptionMember.SECOND_GET_QUOTA_ERROR);
        }
        String operationType = record.getOperationType();
        BigDecimal useQuota = record.getUseQuota();
        //此乃锁定额度
        //得到剩余额度
        BigDecimal rest = BigDecimal.ZERO;
        BigDecimal receivedQuota = BigDecimalUtils.defZero(multiQuotaCtrlMarketMain.getReceivedQuota());
        BigDecimal lockQuota = BigDecimalUtils.defZero(multiQuotaCtrlMarketMain.getLockQuota());
        BigDecimal usedQuota = BigDecimalUtils.defZero(multiQuotaCtrlMarketMain.getUsedQuota());
        BigDecimal allotQuota = BigDecimalUtils.defZero(multiQuotaCtrlMarketMain.getAllotQuota());
        if(receivedQuota == null || BigDecimalUtils.compareToZero(receivedQuota)){
            throw new CustomException(ExceptionMember.REST_QUOTA_ERROR);
        }
        //1锁定额度 2直接扣减
        if(MarketConstant.OPERATION_TYPE_1.equals(operationType)
                || MarketConstant.OPERATION_TYPE_2.equals(operationType)){
            BigDecimal add = BigDecimalUtils.adds(lockQuota,usedQuota,allotQuota,useQuota);
            //如果目前的已锁定额度 + 已使用额度 + 已分配额度 + 即将使用的额度 > 总额度 ，则直接报错
            if(BigDecimalUtils.compareTo(add, receivedQuota) == 1){
                throw new CustomException(ExceptionMember.REST_QUOTA_Not_ENOUGH_ERROR);
            }
        }
        //1锁定额度
        if(MarketConstant.OPERATION_TYPE_1.equals(operationType)){
            lockQuota = BigDecimalUtils.add(lockQuota, useQuota);
            multiQuotaCtrlMarketMain.setLockQuota(lockQuota);
            //2扣减额度
        }else if(MarketConstant.OPERATION_TYPE_2.equals(operationType)){
            usedQuota = BigDecimalUtils.add(usedQuota, useQuota);
            multiQuotaCtrlMarketMain.setUsedQuota(usedQuota);
            //3回冲锁定额度
        }else if(MarketConstant.OPERATION_TYPE_3.equals(operationType)){
            lockQuota = BigDecimalUtils.sub(lockQuota, useQuota);
            if(BigDecimalUtils.compareTo(BigDecimal.ZERO, lockQuota) == 1){
                throw new CustomException(ExceptionMember.RECHARGE_QUOTA_ERROR_3);
            }
            multiQuotaCtrlMarketMain.setLockQuota(lockQuota);
            //4回冲扣减额度
        }else if(MarketConstant.OPERATION_TYPE_4.equals(operationType)){
            usedQuota = BigDecimalUtils.sub(usedQuota, useQuota);
            if(BigDecimalUtils.compareTo(BigDecimal.ZERO, usedQuota) == 1){
                throw new CustomException(ExceptionMember.RECHARGE_QUOTA_ERROR_4);
            }
            multiQuotaCtrlMarketMain.setUsedQuota(usedQuota);
        }
        //保存到缓存中
        multiQuotaCtrlMarketMainService.saveMultiQuotaCtrlMarketMainToRedis(multiQuotaCtrlMarketMain);
        return multiQuotaCtrlMarketMain;
    }

    /**
     * 从缓存中获取额度信息，如果额度不存在，则从数据库中获取
     *
     * @param record 记录
     * @return boolean
     */
    @Override
    @CustomCacheable(value = "quota",key="'cache:quota:' + #record.type + ':' + #record.dataType + ':' + #record.dataId")
    public MultiQuotaCtrlMarketMain getMultiQuotaCtrlMarketMainForRedis(QuotaBaseIO record){
        MultiQuotaCtrlMarketMainIO record1 = (MultiQuotaCtrlMarketMainIO)record;
        //当进入这个方法时，则表示当前的额度信息在缓存中不存在
        return multiQuotaCtrlMarketMainService.getMultiQuotaCtrlMarketMainForDB(record1);
    }

    /**
     * 修正缓存
     *
     * @param record 记录
     * @return record
     */
    @Override
    @CustomCachePut(value = "quota",key="'cache:quota:' + #record.type + ':' + #record.dataType + ':' + #record.dataId")
    public MultiQuotaCtrlMarketMain saveMultiQuotaCtrlMarketMainToRedis(MultiQuotaCtrlMarketMain record){
        return record;
    }

    @Override
    public MultiQuotaCtrlMarketMain getMultiQuotaCtrlMarketMainForRedis(MultiQuotaCtrlMarketMainIO record) {
        return null;
    }


    /**
     * 从数据库中获取额度数据，拦截其他请求，使其他请求快速失败
     *
     * @param record 记录
     * @return boolean
     */
    @Override
    @CustomLock(key = "'quota:' + #record.type + ':' + #record.dataType + ':' + #record.dataId",isFailFast = true)
    public MultiQuotaCtrlMarketMain getMultiQuotaCtrlMarketMainForDB(MultiQuotaCtrlMarketMainIO record){
        MultiQuotaCtrlMarketMain multiQuotaCtrlMarketMain = multiQuotaCtrlMarketMainMapper.selectByVo(record);
        return multiQuotaCtrlMarketMain;
    }

}
